# frozen_string_literal: true

module Gitlab
  module Llm
    module Templates
      class ExplainVulnerability
        include Gitlab::Utils::StrongMemoize

        MAX_CODE_LENGTH = 1_024
        SECRET_PATTERNS = [
          /secret/i,
          /key/i,
          /ENV/i,
          /[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/i,
          /token/i,
          /'Bearer\s[\d|a-f]{8}-([\d|a-f]{4}-){3}[\d|a-f]{12}'/i,
          /password/i,
          /encrypted/i,
          /encryption/i,
          /email/i,
          /authorisation/i,
          /authorization/i,
          /session/i,
          /cert/i,
          /jwt/i,
          /otp/i,
          /signing/i,
          /dsn/i,
          /trace/i
        ].freeze

        def initialize(vulnerability, params = {})
          @vulnerability = vulnerability
          @params = params
        end

        def to_prompt(include_source_code: nil)
          prompt(include_source_code: include_source_code)
        end

        def presubmission_checks
          {
            potential_secrets_in_code: unsafe_code?,
            secret_detection_result: vulnerability.secret_detection?
          }
        end

        private

        attr_reader :vulnerability

        delegate :title, :description, :file, to: :vulnerability
        delegate :source_code?, :vulnerable_code, to: :finding

        # This will always provide a prompt by default, but if
        # `include_source_c45ode` is set, we'll respond with nil if no
        # code is available to provide a better interface to the
        # frontend.
        def prompt(include_source_code: false)
          case include_source_code
          when true
            prompt_with_code if eligible_code?
          when false
            prompt_without_code
          else
            eligible_and_safe_code? ? prompt_with_code : prompt_without_code
          end
        end

        # rubocop: disable CodeReuse/ActiveRecord
        def identifiers
          finding.identifiers.pluck(:name).join(", ")
        end
        # rubocop: enable CodeReuse/ActiveRecord

        def finding
          vulnerability.finding
        end
        strong_memoize_attr :finding

        def filename
          File.basename(file)
        end

        def eligible_code?
          return false unless source_code?
          return false if vulnerable_code.length > MAX_CODE_LENGTH
          return false if vulnerability.secret_detection?

          true
        end
        strong_memoize_attr :eligible_code?

        def eligible_and_safe_code?
          return false unless presubmission_check_passed?

          eligible_code?
        end
        strong_memoize_attr :eligible_and_safe_code?

        def presubmission_check_passed?
          !unsafe_code?
        end

        # In case of possible secret patterns in the code snippet, we aggressively
        # ban certain patterns until a better solution is in place.
        # See https://gitlab.com/gitlab-org/gitlab/-/issues/413112
        def unsafe_code?
          SECRET_PATTERNS.any? do |pattern|
            pattern.match?(vulnerable_code)
          end
        end

        def prompt_with_code
          <<~PROMPT
          You are a software vulnerability developer.
          Explain the vulnerability "#{title} - (#{identifiers})".
          The file "#{filename}" has this vulnerable code:

          ```
          #{vulnerable_code}
          ```

          Provide a code example with syntax highlighting on how an attacker can take advantage of the vulnerability.
          Provide a code example with syntax highlighting on how to fix it.
          Provide the response in markdown format with headers.
          PROMPT
        end

        def prompt_without_code
          return prompt_without_file_or_code unless file.present?

          <<~PROMPT
          You are a software vulnerability developer.
          Explain the vulnerability "#{title} - (#{identifiers})".
          The vulnerable code is in the file "#{filename}".
          Provide a code example with syntax highlighting on how an attacker can take advantage of the vulnerability.
          Provide a code example with syntax highlighting on how to fix it.
          Provide the response in markdown format with headers.
          PROMPT
        end

        def prompt_without_file_or_code
          <<~PROMPT
          You are a software vulnerability developer.
          Explain the vulnerability "#{title} - (#{identifiers})".
          Provide a code example with syntax highlighting on how an attacker can take advantage of the vulnerability.
          Provide a code example with syntax highlighting on how to fix it.
          Provide the response in markdown format with headers.
          PROMPT
        end
      end
    end
  end
end
